﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CivLib.CivNum;

namespace CivLib.CivHyd
{
  /// <summary>
  /// Base object for storing stuff
  /// </summary>
  public abstract class BaseStorageObj : HydObj
  {
  }

  /// <summary>
  /// Defines a Trapazoidal Detention Pond with a given planar top
  /// </summary>
  public class TrapDetPond : BaseStorageObj
  {
    #region contained variables
    double m_sideSlope;
    double m_depth;
    int m_stageSeriesSize;
    CivSpatial.PlanarObject m_top;
    StageSeries m_stageStorDisch;
    OutletList m_outlets;
    #endregion

    #region constructor
    /// <summary>
    /// Constructor for Trapezoidial Detention Pond
    /// </summary>
    /// <param name="a_top">2D Planar Object that defines the top</param>
    /// <param name="a_depth">desired depth of the pond, changed to a maxium depth if not possible</param>
    /// <param name="a_sideslope">side slope of the pond in the form H:V e.g. 2 for 2:1</param>
    /// <param name="a_outlets">the list of outlets for the pond</param>
    public TrapDetPond(CivSpatial.PlanarObject a_top,
      double a_depth, double a_sideslope, OutletList a_outlets)
    {
      m_top = a_top;
      m_depth = a_depth;
      m_sideSlope = a_sideslope;
      m_outlets = a_outlets;
      m_stageSeriesSize = 20;
      double maxDepth = MaxPossibleDepth();
      if (m_depth > maxDepth)
      {
        m_depth = maxDepth;
      }
    }
    #endregion

    /// <summary>
    /// Calculates the stage series for the pond
    /// </summary>
    public void CalcStageStorageDischarge()
    {
      //this constructor initializes the stage steps
      m_stageStorDisch = new StageSeries(m_stageSeriesSize, m_depth);
      double[] beginningDimensions = new double[m_top.DimCount];

      //save all of the beginning dimensions to restore later
      for (int i = 0; i < m_top.DimCount; i++)
      {
        beginningDimensions[i] = m_top.Dim[i];
      }

      //get the interval for the depth
      double depthInterval = m_stageStorDisch.StageInc;
      double topArea, bottomArea, offsetInt;
      topArea = m_top.Area();
      offsetInt = depthInterval * m_sideSlope;

      //fill in the stage storage curve
      for (int i = 0; i < m_stageStorDisch.Count; i++)
      {
        double stage = m_stageStorDisch.Stage[i];
        m_stageStorDisch.Discharge[i] = m_outlets.FlowAtDepth(stage);

        m_top.Offset(-offsetInt);
        bottomArea = m_top.Area();
        m_stageStorDisch.Storage[m_stageSeriesSize - i - 1] =
          SpatMath.FrustVol(topArea, bottomArea, depthInterval);
        topArea = bottomArea;
      }

      //reset all of the top dimensions
      for (int i = 0; i < m_top.DimCount; i++)
      {
        m_top.Dim[i] = beginningDimensions[i];
      }
    }

    double MaxPossibleDepth()
    {
      double maxDepth;
      List<double> sortedDim = new List<double>();

      for (int i = 0; i < m_top.DimCount; i++)
      {
        sortedDim.Add(m_top[i]);
      }
      sortedDim.Sort();
      maxDepth = sortedDim[1] / m_sideSlope;

      return maxDepth;
    }

  }

}//CivLib.CivHyd
